// Created by Windward Studios - no copyright is claimed. This code can be used in
// any manner by anyone for any reason. There is no copyright of any kind on it. You may
// use it in commercial products. You may change it without sharing those changes.
// We ask that you keep the "created by Windward Studios" in a comment at the top.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Kailua.net.windward.utils;

namespace net.windward.utils.ado.Oracle
{
	/// <summary>
	/// A Oracle database.
	/// Note that all the calls to GetColumn could be done using a single SQL statement, if speed improvements are needed.
	/// </summary>
	public class WrOracleDatabase : IWrDatabase
	{
		private readonly DbProviderFactory provider;
		private readonly string server;
		private WrTable[] tables;
		private WrView[] views;
        private WrStoredProcedure[] storedProcedures;
	    private ArrayList keyRelationships = new ArrayList();
        private List<StoredProcedureParameter> storedProcedureRelationships = new List<StoredProcedureParameter>();
	    private string currentSchema;

		private static readonly Hashtable types;
		static WrOracleDatabase()
		{
			types = new Hashtable();
            // Strings
            types.Add("varchar2", DbType.String);
            types.Add("nvarchar2", DbType.String);
            types.Add("char", DbType.String);
            types.Add("nchar", DbType.String);
            types.Add("clob", DbType.String);
            types.Add("nclob", DbType.String);
            // Dates
            types.Add("date", DbType.DateTime);
            types.Add("timestamp(6)", DbType.DateTime);
            types.Add("interval", DbType.Object);
            // Numbers
            types.Add("binary_float", DbType.Single);
            types.Add("binary_double", DbType.Double);
            types.Add("number", DbType.VarNumeric);
            // Misc.
            types.Add("blob", DbType.Binary);
            types.Add("raw", DbType.Binary);
            types.Add("bfile", DbType.Object);
            types.Add("rowid", DbType.AnsiString);
            types.Add("urowid", DbType.AnsiString);
            // Deprecated
            types.Add("long", DbType.String);
            types.Add("varchar", DbType.String);
		}

		/// <summary>
		/// Create a Oracle database object.
		/// </summary>
		/// <param name="provider">The provider for this server.</param>
		/// <param name="server">The name of the server the database is on.</param>
		public WrOracleDatabase(DbProviderFactory provider, string server)
		{
			this.provider = provider;
			this.server = server;
		}

		/// <summary>
		/// The name of the database.
		/// </summary>
		public string Name
		{
			get { return ""; }
		}

		/// <summary>
		/// The default schema for all objects in this database. Returns null if there is no default.
		/// </summary>
		public string DefaultSchema
		{
			get
			{
				return null;
			}
		}

		#region IWrDatabase Members

		/// <summary>
		/// All tables in this database. Must call LoadMetadata first.
		/// </summary>
		public WrTable[] Tables
		{
			get { return tables; }
		}

		/// <summary>
		/// All views in this database. Must call LoadMetadata first.
		/// </summary>
		public WrView[] Views
		{
			get { return views; }
		}

		/// <summary>
		/// Return all stored procedures in this database. Must call LoadMetadata first.
		/// </summary>
		public WrStoredProcedure[] StoredProcedures
		{
			get { return storedProcedures; }
		}

		/// <summary>
		/// Loads the metadata for this database.
		/// </summary>
		/// <param name="credentials">The user credentials to access the database.</param>
		/// <param name="owner">Determines what metadata objects are read and if full metadata is retrieved.</param>
		/// <param name="status">Pass the status of loading the metadata.</param>
		public void LoadMetadata(WrCredentials credentials, WrVendor.ELEM_OWNER owner, WrDatabase.LoadStatus status)
		{
			LoadMetadata(ConnectionString(credentials), owner, status);
		}

		/// <summary>
		/// Loads the metadata for this database.
		/// </summary>
		/// <param name="connStr">The connection string to access the database.</param>
		/// <param name="owner">Determines what metadata objects are read and if full metadata is retrieved.</param>
		/// <param name="status">Pass the status of loading the metadata.</param>
		public void LoadMetadata(string connStr, WrVendor.ELEM_OWNER owner, WrDatabase.LoadStatus status)
		{
		    GetCurrentSchema(connStr);
		    LoadFkPkMappings(connStr);
			tables = GetTables(connStr, owner);
			for (int index = 0; index < tables.Length; index++)
			{
				WrColumn[] columns = GetColumns(tables[index], connStr, owner);
				tables[index] = new WrTable(tables[index].Schema, tables[index].Name, tables[index].Description, tables[index].IsSystem, columns);
			}

			views = GetViews(connStr, owner);
			for (int index = 0; index < views.Length; index++)
			{
				WrColumn[] columns = GetColumns(views[index], connStr, owner);
				views[index] = new WrView(views[index].Schema, views[index].Name, views[index].Description, views[index].IsSystem, columns);
			}

		    LoadStoredProcedureMappings(connStr);
            storedProcedures = GetStoredProcedures(connStr, owner, status);
		}

		private const string whereUser = "WHERE OWNER NOT IN ('SYSAUX','SYSTEM','SYS','MDSYS','EXFSYS','WMSYS','CTXSYS','XDB','ORDSYS','DMSYS','OLAPSYS')";
		private const string whereNotUser = "WHERE OWNER IN ('SYSAUX','SYSTEM','SYS','MDSYS','EXFSYS','WMSYS','CTXSYS','XDB','ORDSYS','DMSYS','OLAPSYS')";

		/// <summary>
		/// Return all tables in this database.
		/// </summary>
		/// <param name="connStr">The connection string to access the database.</param>
		/// <param name="owner">Determines what tables are returned and if metadata is also retrieved.</param>
		/// <returns>all tables in this database.</returns>
		private WrTable[] GetTables(string connStr, WrVendor.ELEM_OWNER owner)
		{
			try
			{
				using (DbConnection conn = provider.CreateConnection())
				{
					conn.ConnectionString = connStr;
					conn.Open();
					using (DbCommand cmd = conn.CreateCommand())
					{
						cmd.CommandType = CommandType.Text;
						switch (owner & WrVendor.ELEM_OWNER.ALL)
						{
							case WrVendor.ELEM_OWNER.USER :
								cmd.CommandText = "SELECT TABLE_NAME,OWNER FROM ALL_TABLES " + whereUser + " ORDER BY TABLE_NAME";
								break;
							case WrVendor.ELEM_OWNER.SYSTEM:
								cmd.CommandText = "SELECT TABLE_NAME,OWNER FROM ALL_TABLES " + whereNotUser + " ORDER BY TABLE_NAME";
								break;
							case WrVendor.ELEM_OWNER.ALL:
								cmd.CommandText = "SELECT TABLE_NAME,OWNER FROM ALL_TABLES ORDER BY TABLE_NAME";
								break;
							case WrVendor.ELEM_OWNER.USER_OWNED:
								cmd.CommandText = "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME";
								break;
						}
						ArrayList rtn = new ArrayList();
						using (IDataReader reader = cmd.ExecuteReader())
						{
                            while (reader.Read())
                            {
                                if ((owner & WrVendor.ELEM_OWNER.ALL) == WrVendor.ELEM_OWNER.USER_OWNED)
                                    rtn.Add(new WrTable(null, reader.GetString(0), "", false, null));
                                else
                                    rtn.Add(new WrTable(reader.GetString(1), reader.GetString(0), "", false, null));
                            }
						}
						return (WrTable[]) rtn.ToArray(typeof (WrTable));
					}
				}
			}
			catch (Exception)
			{
				return new WrTable[0];
			}
		}

		/// <summary>
		/// Return all views in this database.
		/// </summary>
		/// <param name="connStr">The connection string to access the database.</param>
		/// <param name="owner">Determines what views are returned and if metadata is also retrieved.</param>
		/// <returns>all views in this database.</returns>
		private WrView[] GetViews(string connStr, WrVendor.ELEM_OWNER owner)
		{
			try
			{
				using (DbConnection conn = provider.CreateConnection())
				{
					conn.ConnectionString = connStr;
					conn.Open();
					using (DbCommand cmd = conn.CreateCommand())
					{
						cmd.CommandType = CommandType.Text;
						switch (owner & WrVendor.ELEM_OWNER.ALL)
						{
							case WrVendor.ELEM_OWNER.USER:
								cmd.CommandText = "SELECT VIEW_NAME FROM ALL_VIEWS " + whereUser + " ORDER BY VIEW_NAME";
								break;
							case WrVendor.ELEM_OWNER.SYSTEM:
								cmd.CommandText = "SELECT VIEW_NAME FROM ALL_VIEWS " + whereNotUser + " ORDER BY VIEW_NAME";
								break;
							case WrVendor.ELEM_OWNER.ALL:
								cmd.CommandText = "SELECT VIEW_NAME FROM ALL_VIEWS ORDER BY VIEW_NAME";
								break;
							case WrVendor.ELEM_OWNER.USER_OWNED:
								cmd.CommandText = "SELECT VIEW_NAME FROM USER_VIEWS ORDER BY VIEW_NAME";
								break;
						}
						ArrayList rtn = new ArrayList();
						using (IDataReader reader = cmd.ExecuteReader())
						{
							while (reader.Read())
								rtn.Add(new WrView(null, reader.GetString(0), "", false, null));
						}
						return (WrView[]) rtn.ToArray(typeof (WrView));
					}
				}
			}
			catch (Exception)
			{
				return new WrView[0];
			}
		}

		/// <summary>
		/// Return all columns in a table or view.
		/// </summary>
		/// <param name="table">The name of the table or view.</param>
		/// <param name="connStr">The connection string to access the database.</param>
		/// <param name="owner">Determines what columns are returned and if metadata is also retrieved.</param>
		/// <returns>all columns in a table or view.</returns>
        public WrColumn[] GetColumns(WrBaseTable table, string connStr, WrVendor.ELEM_OWNER owner)
        {
            try
            {
                using (DbConnection conn = provider.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandType = CommandType.Text;
                        cmd.CommandText = "SELECT COLUMN_NAME, DATA_TYPE, DATA_PRECISION, DATA_SCALE, OWNER FROM ALL_TAB_COLUMNS where TABLE_NAME=:p0";
                        SetParameter(cmd, 0, table.FullName);
                        ArrayList rtn = new ArrayList();
                        using (IDataReader reader = cmd.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                String columnOwner = reader.GetString(4);
                                if (columnOwner.Equals(table.Schema) || columnOwner.Equals(currentSchema))
                                {
                                    string type = reader.GetString(1).ToLower();
                                    // can be int or decimal
                                    object o = reader.IsDBNull(2) ? -1 : reader.GetValue(2);
                                    int precision = o is decimal ? (int) ((decimal) o) : (o is int ? (int) o : -1);
                                    o = reader.IsDBNull(3) ? -1 : reader.GetValue(3);
                                    int scale = o is decimal ? (int) ((decimal) o) : (o is int ? (int) o : -1);
                                    rtn.Add(new WrColumn(reader.GetString(0), "",
                                                         ConvertFromOracleDbType(type, precision, scale)));
                                }
                            }
                        }

                        if ((owner & WrVendor.ELEM_OWNER.KEY_JOINS) != 0)
                        {
                            // set the pk-fk mappings
                            try
                            {
                                FkPkMappings(rtn, table.FullName);
                            }
                            catch (Exception)
                            {
                                // nada
                            	Trap.trap();
                            }
                        }

                        return (WrColumn[])rtn.ToArray(typeof(WrColumn));
                    }
                }
            }
            catch (Exception)
            {
                return new WrColumn[0];
            }
        }

        /// <summary>
        /// Return all stored procedures in this database.
        /// </summary>
        /// <param name="connStr">The connection string to access the database.</param>
        /// <param name="owner">Set if to get all user, all system, or all (both) stored procedures.</param>
        /// <returns>all stored procedures in this database.</returns>
        /// <param name="status">Pass the status of loading the metadata.</param>
        private WrStoredProcedure[] GetStoredProcedures(string connStr, WrVendor.ELEM_OWNER owner, WrDatabase.LoadStatus status)
        {
            List<WrStoredProcedure> rtn = new List<WrStoredProcedure>();

            using (DbCommand cmd = provider.CreateCommand())
            {
                using (DbConnection conn = provider.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    conn.Open();

                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = String.Format("select object_name from user_procedures");

                    using (DbDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            if (reader.GetString(0) != null)
                            {
                                // get parameter information
                                List<WrProcParam> procParams = new List<WrProcParam>();
                                for (int i = 0; i < storedProcedureRelationships.Count; i++)
                                {
                                    if(reader.GetString(0).Equals(storedProcedureRelationships[i].ProcedureName))
                                        procParams.Add(new WrProcParam(storedProcedureRelationships[i].ParameterName, storedProcedureRelationships[i].ParameterType, storedProcedureRelationships[i].ParameterDataLength, storedProcedureRelationships[i].ParameterHasDefault, storedProcedureRelationships[i].ParameterDirection));
                                }

                                rtn.Add(new WrStoredProcedure(currentSchema, reader.GetString(0), "", false, procParams.ToArray()));
                            }
                        }
                    }

                    rtn.Sort();
                    return rtn.ToArray();
                }
            }
        }

		// this is the only code that uses the OracleClient
		private static DbType ConvertFromOracleDbType(string name, int precision, int scale)
		{

			// standard conversions
			string key = name.ToLower();
			if (types.Contains(key))
				return (DbType)types[key];

			// a NUMBER can be lots of things. And this makes assumptions - a byte needs 3 digits (255) but 3 digits could be an int16 (999)
			if (name == "number")
			{
				if (scale > 0)
					return DbType.Decimal;
				switch (precision)
				{
					case 1:
						return DbType.Boolean;
					case 2:
					case 3:
						return DbType.Byte;
					case 4:
					case 5:
						return DbType.Int16;
					case 6:
					case 7:
					case 8:
					case 9:
					case 10:
						return DbType.Int32;
				}
				return DbType.Int64;
			}

			return DbType.Object;

			/* doesn't work - almost nothing can Enum.Parse
			try
			{
				OracleType typ = (OracleType)Enum.Parse(typeof(OracleType), name, true);
				OracleParameter parm = new OracleParameter();
				parm.OracleType = typ;
				return parm.DbType;
			}
			catch (Exception)
			{
				return DbType.Object;
			}
			*/
		}

		#endregion

		private string ConnectionString(WrCredentials credentials)
		{
			return WrOracleVendor.ConnectionString(provider, server, null, credentials, true);
		}

		private void SetParameter(DbCommand cmd, int ind, object value)
		{

			if (value == null)
				value = DBNull.Value;

			DbParameter param = provider.CreateParameter();
			param.ParameterName = ":p" + ind;
			param.Value = value;

			cmd.Parameters.Add(param);
		}

        #region Key relationship helper functions
        /// <summary>
        /// Sets the PrimaryKey of each foreign-key in the specified columns (WrColumn) of the specified table.
        /// </summary>
        /// <param name="columns">The columns.  These must be of type WrColumn</param>
        /// <param name="table">The table's name.</param>
        private void FkPkMappings(IList columns, string table)
        {
            // identify the primary key's table and column for each constrained WrColumn in columns
            for (int ind = 0; ind < columns.Count; ind++)
            {
                WrColumn colOn = (WrColumn)columns[ind];

                string columnName = colOn.FullName;
                string tableName = table;

                // find pk table/col (if it exists)
                foreach (Object obj in keyRelationships)
                {
                    PkFkEntry entry = (PkFkEntry)obj;
                    if (tableName == entry.FkTable)
                        if (columnName == entry.FkColumn)
                            columns[ind] = new WrColumn(colOn.FullName, colOn.Description, colOn.DbType, entry.PkTable + "." + entry.PkColumn);
                }
            }
        }

        /// <summary>
        /// Caches PK-FK information for later use
        /// </summary>
        /// <param name="connStr">The connection string to the target server.</param>
        private void LoadFkPkMappings(string connStr)
        {
            keyRelationships.Clear();

            using (DbCommand cmd = provider.CreateCommand())
            {
                using (DbConnection conn = provider.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    conn.Open();

                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = String.Format(
                        "SELECT r_cols.column_name, r_cols.table_name, cols.column_name, cols.table_name " +
                        "FROM user_cons_columns cols " +
                        "LEFT JOIN all_constraints alc " +
                        "ON alc.constraint_name = cols.constraint_name " +
                        "AND alc.owner = cols.owner " +
                        "LEFT JOIN all_constraints r_alc " +
                        "ON alc.r_constraint_name = r_alc.constraint_name " +
                        "AND alc.r_owner = r_alc.owner " +
                        "LEFT JOIN all_cons_columns r_cols " +
                        "ON r_alc.constraint_name = r_cols.constraint_name " +
                        "AND r_alc.owner = r_cols.owner " +
                        "AND cols.position = r_cols.position " +
                        "WHERE alc.constraint_name = cols.constraint_name " +
                        "AND alc.constraint_type = 'R'");

                    using (DbDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            if (reader.GetString(0) != null && reader.GetString(1) != null && reader.GetString(2) != null && reader.GetString(3) != null)
                                keyRelationships.Add(new PkFkEntry(reader.GetString(0), reader.GetString(1), reader.GetString(2), reader.GetString(3)));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Cached PK-FK relationship
        /// </summary>
        private class PkFkEntry
        {
            private string _pkColumn;
            private string _pkTable;
            private string _fkColumn;
            private string _fkTable;

            public PkFkEntry(string pkc, string pkt, string fkc, string fkt)
            {
                _pkColumn = pkc;
                _pkTable = pkt;
                _fkColumn = fkc;
                _fkTable = fkt;
            }

            public string PkColumn
            {
                get
                {
                    return _pkColumn;
                }
            }

            public string PkTable
            {
                get
                {
                    return _pkTable;
                }
            }

            public string FkColumn
            {
                get
                {
                    return _fkColumn;
                }
            }

            public string FkTable
            {
                get
                {
                    return _fkTable;
                }
            }
        }
        #endregion

        #region Stored procedure parameter helper functions

        /// <summary>
        /// Return all parameters in a stored procedure.
        /// </summary>
        /// <param name="procedure">The name of the stored procedure.</param>
        /// <param name="connStr">The connection string to access the database.</param>
        /// <param name="owner">Determines what columns are returned and if metadata is also retrieved.</param>
        /// <returns>all columns in a table or view.</returns>
        public WrProcParam[] GetParameters(string procedure, string connStr, WrVendor.ELEM_OWNER owner)
        {
            // get all parameter objects needed and conver them to WrProcParam objects
            ArrayList rtn = new ArrayList();
            foreach(StoredProcedureParameter para in storedProcedureRelationships)
            {
                if(procedure.Equals(para.ProcedureName))
                {
                    rtn.Add(new WrProcParam(para.ParameterName, DbType.Object, 0, false, ParameterDirection.Input));
                }
            }
            return (WrProcParam[])rtn.ToArray(typeof(WrProcParam));

           
        }

        /// <summary>
        /// Caches PK-FK information for later use
        /// </summary>
        /// <param name="connStr">The connection string to the target server.</param>
        private void LoadStoredProcedureMappings(string connStr)
        {
            storedProcedureRelationships.Clear();

            using (DbCommand cmd = provider.CreateCommand())
            {
                using (DbConnection conn = provider.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    conn.Open();

                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = String.Format("select object_name, argument_name, data_type, default_value, in_out, data_length, data_precision, data_scale, char_length from user_arguments order by POSITION");
                    //                                           0             1            2            3          4         5             6              7           8

                    using (DbDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            if (reader.GetString(0) != null && reader.GetString(1) != null && reader.GetString(2) != null && reader.GetValue(3) != null && reader.GetString(4) != null && reader.GetValue(5) != null && reader.GetValue(6) != null && reader.GetValue(7) != null && reader.GetValue(8) != null)
                            {
                                String procedureName = reader.GetValue(0) is string ? reader.GetString(0) : null;
                                
                                String parameterName = reader.GetValue(1) is string ? reader.GetString(1): null;
                                
                                String parameterType = reader.GetValue(2) is string ? reader.GetString(2) : null;
                                decimal parameterDataLength = reader.GetValue(5) is decimal ? (decimal)reader.GetValue(5) : 0;
                                decimal parameterDataPrecision = reader.GetValue(6) is decimal ? (decimal)reader.GetValue(6) : 0;
                                decimal parameterDataScale = reader.GetValue(7) is decimal ? (decimal)reader.GetValue(7) : 0;
                                decimal parameterCharLength = reader.GetValue(8) is decimal ? (decimal)reader.GetValue(8) : 0;
                                DbType parameterDbType = ConvertFromOracleDbType(parameterType, (int)parameterDataPrecision, (int)parameterDataScale);

                                bool parameterDefaultValue = !(reader.GetValue(3) is DBNull);
                                
                                ParameterDirection parameterInOut;
                                switch (reader.GetString(4))
                                {
                                    case "IN":
                                        parameterInOut = ParameterDirection.Input;
                                        break;
                                    case "OUT":
                                        parameterInOut = ParameterDirection.Output;
                                        break;
                                    case "IN/OUT":
                                        parameterInOut = ParameterDirection.InputOutput;
                                        break;
                                    default:
                                        continue;
                                }

                                int parameterIntLength = (int)Math.Max(parameterDataLength, parameterCharLength);

                                storedProcedureRelationships.Add(new StoredProcedureParameter(procedureName, parameterName, parameterDbType, parameterIntLength, parameterDefaultValue, parameterInOut));
                            }
                        }
                    }
                }
            }

            /*ArrayList rtn = new ArrayList();
           using (DbConnection conn = provider.CreateConnection())
           {
               conn.ConnectionString = connStr;
               conn.Open();
               // bugbug - set schema
               string[] restrictions = new string[] { null, null, procedure, null };
               DataTable schemaTable = conn.GetSchema("ProcedureParameters", restrictions);

               int ordMode = schemaTable.Columns.IndexOf("PARAMETER_MODE");
               int ordParamName = schemaTable.Columns.IndexOf("PARAMETER_NAME");
               int ordType = schemaTable.Columns.IndexOf("DATA_TYPE");
               int ordLength = schemaTable.Columns.IndexOf("CHARACTER_MAXIMUM_LENGTH");

               foreach (DataRow rowOn in schemaTable.Rows)
               {
                   ParameterDirection dir;
                   switch (TrimString(rowOn, ordMode))
                   {
                       case "IN":
                           dir = ParameterDirection.Input;
                           break;
                       case "INOUT":
                           dir = ParameterDirection.InputOutput;
                           break;
                       case "OUT":
                           dir = ParameterDirection.Output;
                           break;
                       default:
                           Trap.trap();
                           dir = ParameterDirection.InputOutput;
                           break;
                   }
                   int len;
                   if (rowOn[ordLength] is int)
                       len = (int)rowOn[ordLength];
                   else
                       int.TryParse(TrimString(rowOn, ordLength), out len);
                   rtn.Add(new WrProcParam(TrimString(rowOn, ordParamName), ConvertFromSqlDbType(TrimString(rowOn, ordType)), len,
                                           false, dir));
               }
           }
           return (WrProcParam[])rtn.ToArray(typeof(WrProcParam));*/
        }

        /// <summary>
        /// Cached stored procedure parameter
        /// </summary>
        private class StoredProcedureParameter
        {
            private readonly string _procedureName;
            private readonly string _parameterName;
            private readonly DbType _parameterType;
            private readonly int _parameterDataLength;
            private readonly bool _parameterHasDefault;
            private readonly ParameterDirection _parameterDirection;


            public StoredProcedureParameter(string proc, string param, DbType type, int length, bool hasDefault, ParameterDirection direction)
            {
                _procedureName = proc;
                _parameterName = param;
                _parameterType = type;
                _parameterDataLength = length;
                _parameterHasDefault = hasDefault;
                _parameterDirection = direction;
            }

            public string ProcedureName
            {
                get
                {
                    return _procedureName;
                }
            }

            public string ParameterName
            {
                get
                {
                    return _parameterName;
                }
            }

            public DbType ParameterType
            {
                get
                {
                    return _parameterType;
                }
            }

            public int ParameterDataLength
            {
                get
                {
                    return _parameterDataLength;
                }
            }

            public bool ParameterHasDefault
            {
                get
                {
                    return _parameterHasDefault;
                }
            }

            public ParameterDirection ParameterDirection
            {
                get
                {
                    return _parameterDirection;
                }
            }
        }
        #endregion

        /// <summary>
        /// Get current schema (user) name
        /// </summary>
        /// <param name="connStr">The connection string to the target server.</param>
        private void GetCurrentSchema(string connStr)
        {
            using (DbCommand cmd = provider.CreateCommand())
            {
                using (DbConnection conn = provider.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    conn.Open();

                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = String.Format("select sys_context( 'userenv', 'current_schema' ) from dual");

                    using (DbDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            if (reader.GetString(0) != null)
                                currentSchema = reader.GetString(0);
                        }
                    }
                }
            }
        }
	}
}
